﻿using Apewer.Internals;
using Apewer;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;

namespace Apewer.Source
{

    /// <summary>表结构。</summary>
    [Serializable]
    public sealed class TableStructure
    {

        #region Instance

        Type _model = null;
        TableAttribute _table = null;
        ColumnAttribute _key = null;
        ColumnAttribute _flag = null;
        ColumnAttribute[] _columns = null;
        ColumnAttribute[] _fillable = null;

        private TableStructure() { }

        /// <summary>使用此结构的记录模型。</summary>
        public Type Model { get => _model; }

        /// <summary>表特性。</summary>
        public TableAttribute Table { get => _table; }

        /// <summary>列信息。</summary>
        public ColumnAttribute[] Columns { get => _columns; }

        /// <summary>可填充的列信息。</summary>
        public ColumnAttribute[] Fillable { get => _fillable; }

        /// <summary>主键。</summary>
        public ColumnAttribute Key { get => _key; }

        /// <summary>记录标记。</summary>
        public ColumnAttribute Flag { get => _flag; }

        /// <summary>从 <see cref="TableStructure"/> 到 Boolean 的隐式转换，判断 <see cref="TableStructure"/> 有效。</summary>
        /// <remarks>True：存在有效结构时；<br />False：不存在有效结构。</remarks>
        public static implicit operator bool(TableStructure instance)
        {
            if (instance == null) return false;
            if (instance._model == null) return false;
            if (instance._columns == null) return false;
            return true;
        }

        #endregion

        #region Obsolete

        /// <summary>不依赖 Record 公共属性。</summary>
        /// <remarks>注意：此属性即将弃用，应改为使用 Table.Independent 属性。</remarks>
        public bool Independent { get => _table == null ? false : _table.Independent; }

        /// <summary>表的说明信息。</summary>
        /// <remarks>注意：此属性即将弃用，应改为使用 Table.Description 属性。</remarks>
        public string Description { get => _table == null ? null : _table.Description; }

        /// <summary>表特性。</summary>
        /// <remarks>注意：此属性即将弃用，应改为使用 Table 属性。</remarks>
        public TableAttribute Attribute { get => _table; }

        /// <summary>使用模型的所有属性，对缺少 Column 特性的属性使用默认参数的 Column 特性。</summary>
        /// <remarks>注意：此属性即将弃用，应改为使用 Table.AllProperties 属性。</remarks>
        public bool AllProperties { get => _table == null ? false : _table.AllProperties; }

        /// <summary>表名。</summary>
        /// <remarks>注意：此属性即将弃用，应改为使用 Table.Name 属性。</remarks>
        public string TableName { get => _table == null ? null : _table.Name; }

        #endregion

        #region TableStructure

        private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();

        /// <summary>解析表结构。</summary>
        public static TableStructure Parse<T>(bool useCache = true, bool force = false) where T : IRecord => Parse(typeof(T), useCache, force);

        /// <summary>解析表结构。</summary>
        /// <returns>表结构。类型不可用于表结构时返回 NULL 值。</returns>
        public static TableStructure Parse(Type model, bool useCache = true, bool force = false)
        {
            var type = model;
            if (type == null || !type.IsClass || type.IsAbstract) return null;

            // 使用缓存。
            var cacheKey = type.FullName;
            if (force) cacheKey = "[force] " + cacheKey;
            if (useCache)
            {
                lock (_tsc)
                {
                    TableStructure cached;
                    if (_tsc.TryGetValue(cacheKey, out cached)) return cached;
                }
            }

            // 解析 TableAttribute。
            var ta = TableAttribute.Parse(type, useCache, force);
            if (!ta && !force) return null;

            // 类型。
            var isRecord = RuntimeUtility.IsInherits(type, typeof(Record));
            var properties = type.GetProperties();
            var total = properties.Length;

            // 解析 ColumnAttribute。
            var key = null as ColumnAttribute;
            var flag = null as ColumnAttribute;
            var columns = new ColumnAttribute[total];
            var columnsCount = 0;
            var fillable = new List<ColumnAttribute>(total);
            if (total > 0)
            {
                var caForce = force || (ta ? ta.AllProperties : false);
                var addedFields = new List<string>(total);
                foreach (var property in properties)
                {
                    var ca = ColumnAttribute.Parse(property, caForce);
                    if (ca != null)
                    {
                        // 检查 field 重复，只保留第一个。
                        var field = ca.Field;
                        if (!addedFields.Contains(field))
                        {
                            addedFields.Add(field);
                            columns[columnsCount] = ca;
                            columnsCount += 1;

                            if (isRecord)
                            {
                                if (property.Name == "Key")
                                {
                                    key = ca;
                                    if (ta != null && ta.PrimaryKey) ca.SetPrimaryKey();
                                }
                                else if (property.Name == "Flag")
                                {
                                    flag = ca;
                                }
                            }
                        }

                        // 可查询的列。
                        fillable.Add(ca);
                        continue;
                    }

                    // 可查询的列。
                    if (!caForce)
                    {
                        ca = ColumnAttribute.Parse(property, caForce);
                        if (ca) fillable.Add(ca);
                    }
                }
            }
            if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount);

            // 排序，将 Key 和 Flag 排在最前。
            columns = ColumnAttribute.Sort(columns);

            // 返回结果。
            var ts = new TableStructure();
            ts._table = ta;
            ts._key = key;
            ts._flag = flag;
            ts._columns = columns;
            ts._model = model;
            ts._fillable = fillable.ToArray();

            // 加入缓存。
            if (useCache)
            {
                lock (_tsc)
                {
                    if (!_tsc.ContainsKey(cacheKey)) _tsc.Add(cacheKey, ts);
                }
            }

            return ts;
        }

        #endregion

        #region TableAttribute

        // 限定表名称/列名称。
        static string RestrictName(string name, bool underline = false, bool english = false)
        {
            if (string.IsNullOrEmpty(name)) return null;
            var str = name;

            // 限定名称仅使用英文和数字。
            if (english)
            {
                const string available = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
                var chars = new ArrayBuilder<char>();
                var strChars = str.ToCharArray();
                var strLength = strChars.Length > 255 ? 255 : strChars.Length;
                for (var i = 0; i < strLength; i++)
                {
                    if (available.IndexOf(strChars[i]) > -1) chars.Add(strChars[i]);
                }
                str = new string(chars.Export());
            }

            // 以下划线开始。
            if (underline)
            {
                if (!str.StartsWith("_")) str = TextUtility.Merge("_", str);
            }

            return str;
        }

        static IDataParameter CreateParameter(object record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
        {
            var property = ca.Property;
            if (property == null) return null;

            var getter = property.GetGetMethod();
            if (getter == null) return null;

            var value = getter.Invoke(record, null);

            if (ca.Type == ColumnType.Bytes || ca.Type == ColumnType.Integer || ca.Type == ColumnType.Float)
            {
                return callback(new Parameter(ca.Field, value, ca.Type, ca.Length));
            }

            if (ca.Type == ColumnType.DateTime)
            {
                return callback(new Parameter(ca.Field, value, ca.Type, 0));
            }

            if (property.PropertyType.Equals(typeof(String)))
            {
                var text = value as string;
                if (text == null) text = "";
                if (ca.Length > 0)
                {
                    switch (ca.Type)
                    {
                        case ColumnType.VarChar:
                        case ColumnType.NVarChar:
                            text = TextUtility.Left(text, ca.Length);
                            break;
                        case ColumnType.VarChar191:
                        case ColumnType.NVarChar191:
                            text = TextUtility.Left(text, 191);
                            break;
                    }
                }
                return callback(new Parameter(ca.Field, text, ca.Type, ca.Length));
            }

            var defaultText = (value == null) ? TextUtility.Empty : value.ToString();
            return callback(new Parameter(ca.Field, defaultText, ca.Type, ca.Length));
        }

        /// <summary>生成 IDataParameter 列表，用于 Insert 和 Update 方法。</summary>
        public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, IEnumerable<string> excludeds)
        {
            if (record == null || callback == null) return null;

            var list = new List<IDataParameter>(_columns.Length);
            foreach (var ca in Columns)
            {
                if (ca == null) continue;

                var parameter = CreateParameter(record, ca, callback);
                if (parameter == null) continue;

                var add = true;
                if (excludeds != null)
                {
                    var lower = parameter.ParameterName.ToLower();
                    foreach (var excluded in excludeds)
                    {
                        if (string.IsNullOrEmpty(excluded)) continue;
                        if (lower == excluded.ToLower())
                        {
                            add = false;
                            break;
                        }
                    }
                }

                if (add) list.Add(parameter);
            }

            return list.ToArray();
        }

        #endregion

    }

}
